package com.swak.utils.ring;

import java.util.List;

import com.swak.utils.Ints;
import com.swak.utils.Lists;
import com.swak.utils.ring.RingBuffer.Wrap;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;

/**
 * RingBuffer.
 * 
 * @author 618lf
 */
public class RingBuffer<E> extends AbstractRingBuffer<Wrap<E>> {

	public RingBuffer(int bufferSize) {
		super(bufferSize);
	}

	/**
	 * 是否已经满了
	 * 
	 * @param read  读指针
	 * @param write 写指针
	 * @return
	 */
	protected boolean isFull(long read, long write) {
		int writeIndex = (int) (write & this.indexMask);
		int readIndex = (int) (read & this.indexMask);
		return writeIndex == readIndex && write > read;
	}

	/**
	 * 是否已经空了
	 * 
	 * @param read 读指针
	 * @param write 写指针
	 * @return
	 */
	protected boolean isEmpty(long read, long write) {
		return write == read;
	}

	/**
	 * 计算写边界
	 * 
	 * @param read 读指针
	 * @param write 写指针
	 * @param wnd 窗口大小
	 * @return
	 */
	protected long writeBound(long read, long write, int wnd) {
		long bound = write + wnd;
		int writeIndex = (int) (write & this.indexMask);
		int readIndex = (int) (read & this.indexMask);
		long maxBound = write - (writeIndex - readIndex)
				+ this.bufferSize * ((writeIndex == readIndex && write > read) || (writeIndex < readIndex) ? 0 : 1);
		return bound <= maxBound ? bound : maxBound;
	}

	/**
	 * 计算实时窗口
	 * 
	 * @param read 读指针
	 * @param write 写指针
	 * @param wnd 窗口大小
	 * @return
	 */
	protected int calCwnd(long read, long write, int wnd) {
		long bound = this.writeBound(read, write, wnd);
		int cwnd = (int) (bound - write);
		return Ints.bound(0, cwnd, wnd);
	}

	/**
	 * 清空
	 * @return
	 */
	protected List<E> clear() {
		List<E> clears = Lists.newArrayList();
		for (int i = 0; i < this.bufferSize; i++) {
			E data = this.del(i);
			if (data != null) {
				clears.add(data);
			}
		}
		return clears;
	}

	/**
	 * 存数据.
	 * 
	 * 返回值：
	 * 
	 * non-null： 重复的数据
	 * null：非重复的数据
	 * 
	 * @param sequence 存储的序列号
	 * @param data 数据 
	 * @return 
	 */
	protected E put(long sequence, E data) {
		Wrap<E> wrap = this.elementAt(sequence);
		E old = wrap.data;
		if (old != null && old.equals(data)) {
			return old;
		}
		wrap.data = data;
		return null;
	}

	/**
	 * 取数据.
	 * 
	 * @param sequence
	 * @return
	 */
	protected E get(long sequence) {
		Wrap<E> wrap = this.elementAt(sequence);
		return wrap.data;
	}

	/**
	 * 删除数据
	 * 
	 * @param sequence
	 * @return
	 */
	protected E del(long sequence) {
		Wrap<E> wrap = this.elementAt(sequence);
		E data = wrap.data;
		wrap.data = null;
		return data;
	}

	@Override
	protected Wrap<E> newInstance() {
		return new Wrap<E>();
	}

	@Getter
	@Setter
	@Accessors(chain = true)
	@ToString
	static class Wrap<E> {
		private E data;
	}
}
